import numpy as np

# 1.向量
'''
几何意义
数据矩阵：行-样本数量，列-特征数量

行向量
列向量

长度：向量模，欧几里得范数，L2范数

加减法：三角形法则
标量乘法：比例缩放

单位向量
全0向量，全1向量


◄ matplotlib.pyplot.quiver() 绘制箭头图
◄ numpy.add() 向量/矩阵加法
◄ numpy.array([[4,3]]) 构造行向量
◄ numpy.array([[4,3]]).T 行向量转置得到列向量
◄ numpy.array([[4], [3]]) 构造列向量
◄ numpy.array([4, 3])[:, None] 构造列向量；按照 [:, None] 形式广播序列；None 代表
numpy.newaxis，增加新维度
◄ numpy.array([4, 3])[:, numpy.newaxis] 构造列向量
◄ numpy.array([4, 3])[None, :] 构造行向量；按照 [None, :] 形式广播序列；None 代表
numpy.newaxis，增加新维度
◄ numpy.array([4, 3])[numpy.newaxis, :] 构造行向量
◄ numpy.array([4,3]) 构造一维序列，严格来说不是行向量
◄ numpy.array([4,3]).reshape((1, -1)) 构造行向量
◄ numpy.array([4,3]).reshape((-1,1)) 构造列向量
◄ numpy.array([4,3], ndmin=2) 构造行向量
◄ numpy.linalg.norm() 默认计算 L2 范数
◄ numpy.ones() 生成全 1 向量/矩阵
◄ numpy.r_[] 将一系列的序列合并；'r' 设定结果以行向量 (默认) 展示，比如
numpy.r_[numpy.array([1,2]), 0, 0, numpy.array([4,5])] 默认产生行向量
◄ numpy.r_['c', [4,3]] 构造列向量
◄ numpy.subtract() 向量/矩阵减法
◄ numpy.zeros() 生成全 0 向量/矩阵
◄ zip(*) 用于将可迭代的对象作为参数，将对象中对应的元素打包成一个个元组，然后返回由这些元组组成的列
表。*代表解包，返回的每一个都是元祖类型，而并非是原来的数据类型
'''
# A = np.array([[1, 2]]) # 行向量
# A1 = np.array([[5, 6]])
# B = np.array([[3], [4]]) # 列向量
# print(A)
# print(B)
# print(A.T) # 转置
# print(A + A1)
# print(A - A1)
# print(A + B)
# print(np.add(A, B))
# print(np.subtract(A, B))
# print(A * 2)
# print(np.linalg.norm(A))
# print(np.ones((3, 2)))
# print(np.zeros((2, 3)))




# 2.向量运算
'''
长度
加减
标量乘法
向量内积 * inner
向量夹角
余弦相似度
向量外积 cross
逐项积
张量积


◄ numpy.arccos() 反余弦
◄ numpy.cross() 计算列向量和行向量的向量积
◄ numpy.dot() 计算向量内积。值得注意的是，如果输入为一维数组，numpy.dot() 输出结果为向量内积；如
果输入为矩阵，numpy.dot() 输出结果为矩阵乘积，相当于矩阵运算符@
◄ numpy.linalg.norm() 计算范数
◄ numpy.multiply() 计算向量逐项积
◄ numpy.outer() 计算外积，张量积
◄ numpy.vdot() 计算两个向量的向量内积。如果输入是矩阵，矩阵会按照先行后列顺序展开成向量之后，再计算
向量内积
◄ scipy.spatial.distance.cosine() 计算余弦距离
◄ seaborn.heatmap() 绘制热图
◄ sklearn.datasets.load_iris() 加载鸢尾花数据
'''
# 向量内积 np.inner()
# 标量积,点积,点乘(.dot,inner)：对应元素积的和，结果为标量
# print(np.inner(A, A1))
# print(np.dot(A, A1.T))
# print(np.vdot(A, A1))

C1 = np.array([[1, 2],
               [3, 4]])
C2 = np.array([[5, 6],
               [7, 8]])
# 矩阵乘积 横*竖的和的矩阵
# print(np.dot(C1, C2))

# print(np.vdot(C1, C2))

# 向量夹角：反余弦
'''
a,b是向量
cosθ = (a*b)/(|a|*|b|)
'''
a, b = np.array([[4], [3]]), np.array([[5], [-2]])
# print(a.T * b)
'''
[[4], [3]]T * [[5], [-2]] = [[4, 3]] * [[5], [-2]] = [[4, 3], * [[5, 5],]
                                                      [4, 3]]   [-2, -2]
'''
# cos_theta = (a.T @ b) / (np.linalg.norm(a, 2) * np.linalg.norm(b, 2))
# cos_radian = np.arccos(cos_theta)
# cos_degree = cos_radian * (180/np.pi)
# print('向量a,b的夹角θ的是: {:.2f}°'.format(cos_degree.item()))

# 余弦相似度和余弦距离
'''
x,q为***列向量***
余弦相似度:
k(x,q) = (x*q)/(|x|*|q|) = (xTq)/(|x|*|q|)
# x*q对应元素积的和
余弦距离：
d(x,q) = 1 - k(x,q)
'''
# x1 = np.array([[5.1, 3.5, 1.4, 0.2]]).T
# x2 = np.array([[4.9, 3.0, 1.4, 0.2]]).T
# k = (x1.T @ x2) / (np.linalg.norm(x1) * np.linalg.norm(x2))
# k = k.item()
# d = 1 - k
# print('x1,x2的余弦相似度为：{},余弦距离为：{}'.format(k, d))

# from scipy.spatial import distance
# from sklearn import datasets
# iris = datasets.load_iris()
# X_data = iris.data[:, :]
# x1_data = X_data[0, :]
# x2_data = X_data[1, :]
# x1_x2_cos_dist = distance.cosine(x1_data, x2_data)
# print(x1_x2_cos_dist)


# 向量外积 x np.cross()
'''
向量积 (vector product) 也叫叉乘 (cross product) 或外积，向量积结果为向量
a x b
右手法则，a x b 的方向同时垂直于a,b
|a x b| = |a||b|sinθ
a = a1i+a2j+a3k
b = b1i+b2j+b3k
|a1 a2 a3|
|b1 b2 b3|
axb =(a2b3-a3b2)i+(a3b1-a1b3)j+(a1b2-a2b1)k
# 排除当前所在列，剩余交叉相乘在相减(↘-↗ + ↗-↘ + ...)
'''
# cross_x1 = np.array([[1, 2, 3]])
# cross_x2 = np.array([[2, 3, 1]])
# x1_cross_x2 = np.cross(cross_x1, cross_x2)
# print(x1_cross_x2)
# cross_x1_col = np.array([[1], [2], [3]])
# cross_x2_col = np.array([[2], [3], [1]])
# x1col_cross_x2col = np.cross(cross_x1_col, cross_x2_col, axis=0)
# print(x1col_cross_x2col)

# 逐项积 ⊙ np.multiply()

# 向量张量积(外积) ⊗ np.outer()
'''
a⊗b = a*b.T
几何视角: a,b张起的一张网
'''
import matplotlib.pyplot as plt
import seaborn as sns
# def plot_heatmap(x,title):
#   fig, ax = plt.subplots()
#   ax = sns.heatmap(x,
#   cmap='RdYlBu_r',
#   cbar_kws={"orientation": "horizontal"}, vmin=-1, vmax=1)
#   ax.set_aspect("equal")
#   plt.title(title)
#   plt.show()
# a = np.array([[0.5],[-0.7],[1],[0.25],[-0.6],[-1]])
# b = np.array([[-0.8],[0.5],[-0.6],[0.9]])
# a_outer_b = np.outer(a, b)
# print(a_outer_b)
# plot_heatmap(a, 'a')
# plot_heatmap(b, 'b')
# plot_heatmap(a_outer_b, 'a_outer_b')



# 3.向量范数
'''
Lp范数
几何角度
  0<p<1 向内凹的四角星形
  p=1 菱形
  p=2 圆形
  p->∞ 趋近于正方形 
L1范数
L2范数
L∞范数

范数、向量内积、矩阵乘法关系：
(‖x‖2)^2 = ‖x‖2 = |<x,x>|^2 == x*x = x.T*x

◄ matplotlib.pyplot.axhline() 绘制水平线
◄ matplotlib.pyplot.axvline() 绘制竖直线
◄ matplotlib.pyplot.contour() 绘制等高线图
◄ matplotlib.pyplot.contourf() 绘制填充等高线图
◄ numpy.abs() 计算绝对值
◄ numpy.linalg.norm() 默认计算 Lp 范数
◄ numpy.linsapce() 指定的间隔内返回均匀间隔的数字
◄ numpy.maximum() 计算最大值
◄ numpy.meshgrid() 生成网格化数据
'''
# Lp范数定义
'''
‖x‖p = (|x1|^p + |x2|^p + ... + |xD|^p)^(1/p)
p->∞时，‖x‖∞ = max(|x1|,|x2|,...,|xD|) #D为3时，呈几何体
‖x‖∞ ≤ ‖x‖2 ≤ ‖x‖1
p为负数时，图像为对应的图像各象限按象限的45°交x,y轴斜线的镜像翻转 
'''
# p_values = [0.05, 0.2, 0.5, 1, 1.5, 2, 4, 8, np.inf]
# x1 = np.linspace(-2.5, 2.5, num=101)
# x2 = x1 
# xx1, xx2 = np.meshgrid(x1, x2)
# fig, axes = plt.subplots(ncols=3,nrows=3,figsize=(12,12))
# for p,ax in zip(p_values,axes.flat):
#   if np.isinf(p):
#     zz = np.maximum(np.abs(xx1),np.abs(xx2))
#   else:
#     zz = ((np.abs(xx1))**p + (np.abs(xx2)**p))**(1./p)
#     # plot contour of Lp
#     ax.contourf(xx1, xx2, zz, 20, cmap='RdYlBu_r')
#     # plot contour of Lp = 1
#     ax.contour (xx1, xx2, zz, [1], colors='k', linewidths = 2)
#     # decorations
#     ax.axhline(y=0, color='k', linewidth = 0.25)
#     ax.axvline(x=0, color='k', linewidth = 0.25)
#     ax.set_xlim(-2.5, 2.5)
#     ax.set_ylim(-2.5, 2.5)
#     ax.spines['top'].set_visible(False)
#     ax.spines['right'].set_visible(False)
#     ax.spines['bottom'].set_visible(False)
#     ax.spines['left'].set_visible(False)
#     ax.set_xlabel('$x_1$')
#     ax.set_ylabel('$x_2$')
#     ax.set_title('p = ' + str(p))
#     ax.set_aspect('equal', adjustable='box')
# plt.show()

# 高斯函数，高斯核函数
'''
高斯函数
f(x) = exp(-x^2)
高斯核函数
Krbf(x,q) = exp(-γ(‖x-q‖2)^2) = exp(-γ‖x-q‖^2)
'''


# 4.矩阵
'''
行列，元素
向量，方阵，长方形矩阵
加减，标量乘法
矩阵乘法
矩阵逆
其他计算：转置，迹，逐项积，行列式值

◄ numpy.add() 矩阵加法运算，等同于 +
◄ numpy.array() 构造多维矩阵/数组
◄ numpy.linalg.det() 计算行列式值
◄ numpy.linalg.inv() 计算矩阵逆
◄ numpy.linalg.matrix_power() 计算矩阵幂
◄ numpy.matrix() 构造二维矩阵
◄ numpy.multiply() 矩阵逐项积
◄ numpy.ones() 生成全 1 矩阵，输入为矩阵形状
◄ numpy.ones_like() 用来生成和输入矩阵形状相同的全 1 矩阵
◄ numpy.subtract() 矩阵减法运算，等同于 -
◄ numpy.trace() 计算矩阵迹
◄ numpy.zeros() 生成零矩阵，输入为矩阵形状
◄ numpy.zeros_like() 用来生成和输入矩阵形状相同的零矩阵
◄ tranpose() 矩阵转置，比如 A.transpose()，等同于 A.T
'''
# 矩阵形状
'''
主对角线：\; 副对角线：/
方阵 n*n的矩阵
对称矩阵：以主对角线镜像对称的矩阵
对角矩阵：主对角线之外全是0的矩阵
单位矩阵: I对角线元素为1，其他为0
三角矩阵：上三角，下三角
'''
# A = np.array([[1, 2, 3],
#                [4, 5, 6],
#                [7, 8, 9]])
# a = np.diag(A)
# A_diag = np.diag(a)

# 矩阵乘法 @ np.matmul
# 横*竖的和 (条件：A的列数=B的行数)
# A = np.array([[1, 2],
#               [3, 4]])
# B = np.array([[2, 4],
#               [1 ,3]])
'''
A @ B = [[1*2+2*1,1*4+2*3], = [[4,10],
         [3*2+4*1,3*4+4*3]]    [10,24]]
'''
# print(np.matmul(A, B))
# A1 = np.array([[1, 2],
#                [3, 4],
#                [0, 0]])
# A2 = np.array([[1, 2, 0],
#                [3, 4, 0]])
# print(A@B)
# print(A1@B) #ok
# print(A2@B) #err

# 矩阵的幂
'''
n 阶方阵矩阵的幂 A^(n+1) = A^n * A
'''
from numpy.linalg import matrix_power as pw
# A = np.array([[1., 2.],
#               [3., 4.]])
# A_3 = pw(A, 3)
# A_3_v3 = A@A@A
# A_3_pw = A**3
# print(A_3)
# print(A_3_v3)
# print(A_3_pw)

# A = np.matrix([[1, 2],
#                [3, 4]])
# B = np.array([[1, 2],
#               [3, 4]])
# print(A**2) # A@A
# print(B**2) # [[bi^2,...]]

# 转置
'''
行列互换
A.T
A.transpose()
'''

# 矩阵逆：相当于矩阵除法
'''
A^(-1)
运算法则：
(A^T)^-1 = (A^-1)^T
(AB)^-1 = (B^-1)(A^-1)
(kA)^-1 = (1/k)A^-1

A^-1 = ([[a, b], [c, d]])^-1 
     = (1/|A|)([[d, -b],[-c, a]])
     = (1/(ad-bc))([[d, -b],[-c, a]])
正交矩阵: A^T = A^-1
'''
# A = np.array([[1, 2],
#               [3, 4]], dtype=np.float32)

# A_inverse = np.linalg.inv(A)
# print(A_inverse)

# 主对角元素之和 (即迹trace) np.trance()

# 逐项积：对应元素相乘 ⊙ np.multiply *
# A = np.array([[1, 2],
#               [3, 4]])
# B = np.array([[5, 6],
#               [7, 8]])
# print(np.multiply(A,B))
# print(A*B)

# 行列式：将矩阵映射到标量 np.linalg.det()
'''
一阶行列式
|a11| = a11
二阶行列式
|a11 a12|
|a21 a22| = a11a22 - a12a21
三阶行列式
|a11 a12 a13|   |a11   0   0|   |0   a12   0|   |0     0 a13|
|a21 a22 a23| = |a21 a22 a23| + |a21 a22 a23| + |a21 a22 a23| = a11|a22 a23| - a12|a21 a23| + a13|a21 a22|
|a31 a32 a33|   |a31 a32 a33|   |a31 a32 a33|   |a31 a32 a33|      |a32 a33|      |a31 a33|      |a31 a32|
对角阵行列式 (三角阵行列式计算同)
|a11 0 0|
|0 a22 0| = a11a22a33
|0 0 a33|
'''
# A = np.array([[4, 2],
#               [1, 3]], dtype=np.float32)
# print(np.linalg.det(A))

# 可以用行列式方法计算向量积
'''
      |i  j  k|
a×b = |-2 1  1| = |1   1|i - |-2 1|j  + |-2 1|k = i-j+3k
      |1 -2 -1|   |-1 -1|    |1 -1|     |1 -2|
'''

# 5.矩阵乘法
'''
向量和向量
矩阵和向量
方阵相关
对角阵
长方形矩阵

◄ numpy.array() 构造多维矩阵/数组
◄ numpy.einsum() 爱因斯坦求和约定
◄ numpy.linalg.inv() 矩阵逆运算
◄ numpy.matrix() 构造二维矩阵
◄ numpy.multiply() 矩阵逐项积
◄ numpy.random.random_integers() 生成随机整数
◄ seaborn.heatmap() 绘制数据热图
'''
# 向量和向量
'''
x,y 为列向量
x·y = y·x = x.T*y = y.T*x
'''

# 向量乘矩阵乘向量：二次型
# 对角阵
# 置换矩阵：每一行每一列恰好有一个1，其余元素为0，置换矩阵的作用是调换元素顺序
# 矩阵乘向量：映射到一维
